package com.example.makerspace.manager;


import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.makerspace.dao.ApplicationDAO;
import com.example.makerspace.dao.NewsDAO;
import com.example.makerspace.domain.ApplicationDO;
import com.example.makerspace.domain.NewsDO;
import com.example.makerspace.dto.*;
import com.example.makerspace.utils.DateUtils;
import com.example.makerspace.utils.FileUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class DataManager {

    public final static String DAY_VISIT = "day_visit_%s_%s";
    public final static String WEEK_VISIT = "week_visit_%s_%s";
    public final static String MONTH_VISIT = "today_visit_%s_%s";
    public final static String TOTAL_VISIT = "total_visit";

    public final static String DAY_APPLICATION = "day_application_%s_%s";
    public final static String APPLICATION_COUNT = "application_count";
    public final static String REGION = "region";
    public final static String PROJECT_TYPE = "project_type";
    public final static String INITIAL_FLAG = "initial_flag";

    public final static Integer LATEST_NUM = 10;

  private  final  static  String   testHttpPrefix="https://119.45.199.13:3035/static/";

   private  final  static  String  devHttpPrefix="https://nism.ytcata.com:443/static/";


    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private ApplicationDAO applicationDAO;

    @Resource
    private NewsDAO newsDAO;



    @PostConstruct
    public void init(){
        Boolean flag = (Boolean) Optional.ofNullable(redisTemplate.opsForValue().get(INITIAL_FLAG)).orElse(Boolean.FALSE);
        if(Boolean.FALSE.equals(flag)){
            List<ApplicationDO> applicationDOS = Optional.ofNullable(applicationDAO.selectAll()).orElse(Collections.emptyList());
            Long totalApplication = (long) applicationDOS.size();
            HashMap<String, String> regionMap = Maps.newHashMap();
            HashMap<String, String> projectTypeMap = Maps.newHashMap();
            for (ApplicationDO applicationDO : applicationDOS) {
                List<String> intentionRegion = applicationDO.getIntentionRegion();
                for (String s : intentionRegion) {
                    Long count = Long.parseLong(regionMap.getOrDefault(s, "0")) + 1L;
                    regionMap.put(s, String.valueOf(count));
                }
                String projectType = applicationDO.getProjectType();
                Long typeCount = Long.parseLong(projectTypeMap.getOrDefault(projectType, "0")) + 1;
                projectTypeMap.put(projectType, String.valueOf(typeCount));
            }
            redisTemplate.opsForValue().set(APPLICATION_COUNT, String.valueOf(totalApplication));
            redisTemplate.opsForValue().set(REGION, regionMap);
            redisTemplate.opsForValue().set(PROJECT_TYPE, projectTypeMap);
            redisTemplate.opsForValue().set(INITIAL_FLAG, Boolean.TRUE);
        }
    }

    public Result visit() {
        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
        calendar.setTime(new Date());
        String dayKey = String.format(DAY_VISIT, calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
        String weekKey = String.format(WEEK_VISIT, calendar.get(Calendar.YEAR), calendar.get(Calendar.WEEK_OF_YEAR));
        String monthKey = String.format(MONTH_VISIT, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH));
        Long day = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(dayKey)).orElse("0")) + 1L;
        Long week = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(weekKey)).orElse("0")) + 1L;
        Long month = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(monthKey)).orElse("0")) + 1L;
        Long total = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(TOTAL_VISIT)).orElse("0")) + 1L;
        redisTemplate.opsForValue().set(dayKey, String.valueOf(day), 2L, TimeUnit.DAYS);
        redisTemplate.opsForValue().set(weekKey, String.valueOf(week), 14L, TimeUnit.DAYS);
        redisTemplate.opsForValue().set(monthKey, String.valueOf(month), 60L, TimeUnit.DAYS);
        redisTemplate.opsForValue().set(TOTAL_VISIT, String.valueOf(total));
        VisitDTO visitDTO = new VisitDTO(total, day, week, month, null);
        return Result.success(visitDTO);
    }


    public Result visitData() {
        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
        calendar.setTime(new Date());
        String dayKey = String.format(DAY_VISIT, calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
        String weekKey = String.format(WEEK_VISIT, calendar.get(Calendar.YEAR), calendar.get(Calendar.WEEK_OF_YEAR));
        String monthKey = String.format(MONTH_VISIT, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH));
        Long day = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(dayKey)).orElse("0"));
        Long week = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(weekKey)).orElse("0"));
        Long month = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(monthKey)).orElse("0"));
        Long total = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(TOTAL_VISIT)).orElse("0"));
        String numberString = String.format("%05d", total);
        List<Character> totalArr = Lists.newArrayList();
        for (char c : numberString.toCharArray()) {
            totalArr.add(c);
        }
        VisitDTO visitDTO = new VisitDTO(total, day, week, month, totalArr);
        return Result.success(visitDTO);
    }

    public Result applicationData() {
        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
        calendar.setTime(new Date());
        String dayApplicationKey = String.format(DAY_APPLICATION, calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
        Long dayApplication = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(dayApplicationKey)).orElse("0"));
        Long applicationCount = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(APPLICATION_COUNT)).orElse("0"));
        HashMap<String, String> regionMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(REGION)).orElse(Maps.newHashMap());
        HashMap<String, String> projectTypeMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(PROJECT_TYPE)).orElse(Maps.newHashMap());
        Long regionCount = (long) regionMap.size();
        Long total = 0L;
        for (String value : projectTypeMap.values()) {
            total += Long.parseLong(value);
        }
        HashMap<String, Long> typeProportion = Maps.newHashMap();
        for (Map.Entry<String, String> entry : projectTypeMap.entrySet()) {
            String type = entry.getKey();
            Long count = Long.parseLong(entry.getValue());
            Long v = Math.round((double) count / (double) total * 100);
            typeProportion.put(type, v);
        }
        total = 0L;
        for (String value : regionMap.values()) {
            total += Long.parseLong(value);
        }
        HashMap<String, Long> regionProportion = Maps.newHashMap();
        for (Map.Entry<String, String> entry : regionMap.entrySet()) {
            String type = entry.getKey();
            Long count = Long.parseLong(entry.getValue());
            Long v = Math.round((double) count / (double) total * 100);
            regionProportion.put(type, v);
        }
        ApplicationDataDTO applicationDataDTO = new ApplicationDataDTO(dayApplication, applicationCount, regionCount, typeProportion, regionProportion);
        return Result.success(applicationDataDTO);
    }

    public void addApplication(ApplicationDTO applicationDTO) {
        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
        calendar.setTime(new Date());
        String dayApplicationKey = String.format(DAY_APPLICATION, calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
        Long dayApplication = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(dayApplicationKey)).orElse("0")) + 1L;
        Long applicationCount = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(APPLICATION_COUNT)).orElse("0")) + 1L;
        HashMap<String, String> regionMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(REGION)).orElse(Maps.newHashMap());
        HashMap<String, String> projectTypeMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(PROJECT_TYPE)).orElse(Maps.newHashMap());
        for (String region : applicationDTO.getIntentionRegion()) {
            Long count = Long.parseLong(regionMap.getOrDefault(region, "0")) + 1L;
            regionMap.put(region, String.valueOf(count));
        }
        String projectType = applicationDTO.getProjectType();
        Long count = Long.parseLong(projectTypeMap.getOrDefault(projectType, "0")) + 1L;
        projectTypeMap.put(projectType, String.valueOf(count));
        redisTemplate.opsForValue().set(dayApplicationKey, String.valueOf(dayApplication));
        redisTemplate.opsForValue().set(APPLICATION_COUNT, String.valueOf(applicationCount));
        redisTemplate.opsForValue().set(REGION, regionMap);
        redisTemplate.opsForValue().set(PROJECT_TYPE, projectTypeMap);
    }


    public void updateApplication(ApplicationDTO applicationDTO, ApplicationDO applicationDO){
        String newProjectType = applicationDTO.getProjectType();
        if(StringUtils.isNotBlank(newProjectType)){
            HashMap<String, String> projectTypeMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(PROJECT_TYPE)).orElse(Maps.newHashMap());
            long count = Long.parseLong(projectTypeMap.get(applicationDO.getProjectType())) - 1L;
            if(count == 0){
                projectTypeMap.remove(applicationDO.getProjectType());
            }else{
                projectTypeMap.put(applicationDO.getProjectType(), String.valueOf(count));
            }
            projectTypeMap.put(newProjectType, String.valueOf(Long.parseLong(projectTypeMap.getOrDefault(newProjectType, "0")) + 1L));
            redisTemplate.opsForValue().set(PROJECT_TYPE, projectTypeMap);
        }

        List<String> intentionRegion = applicationDTO.getIntentionRegion();
        if(CollectionUtils.isNotEmpty(intentionRegion)){
            HashMap<String, String> regionMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(REGION)).orElse(Maps.newHashMap());
            for (String region : applicationDO.getIntentionRegion()) {
                long count = Long.parseLong(regionMap.get(region)) - 1L;
                if(count == 0L){
                    regionMap.remove(region);
                    continue;
                }
                regionMap.put(region, String.valueOf(count));
            }
            for (String region : applicationDTO.getIntentionRegion()) {
                regionMap.put(region, String.valueOf(Long.parseLong(regionMap.getOrDefault(region, "0")) + 1L));
            }
            redisTemplate.opsForValue().set(REGION, regionMap);
        }
    }

    public void deleteApplication(ApplicationDO applicationDO){
        HashMap<String, String> projectTypeMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(PROJECT_TYPE)).orElse(Maps.newHashMap());
        long typeCount = Long.parseLong(projectTypeMap.get(applicationDO.getProjectType())) - 1L;
        if(typeCount == 0L){
            projectTypeMap.remove(applicationDO.getProjectType());
        }else {
            projectTypeMap.put(applicationDO.getProjectType(), String.valueOf(typeCount));
        }
        redisTemplate.opsForValue().set(PROJECT_TYPE, projectTypeMap);

        HashMap<String, String> regionMap = (HashMap<String, String>)Optional.ofNullable(redisTemplate.opsForValue().get(REGION)).orElse(Maps.newHashMap());
        for (String region : applicationDO.getIntentionRegion()) {
            long count = Long.parseLong(regionMap.get(region)) - 1L;
            if(count == 0){
                regionMap.remove(region);
                continue;
            }
            regionMap.put(region, String.valueOf(count));
        }

        Long totalCount = Long.parseLong((String) Optional.ofNullable(redisTemplate.opsForValue().get(APPLICATION_COUNT)).orElse(0L)) - 1L;
        redisTemplate.opsForValue().set(APPLICATION_COUNT, String.valueOf(totalCount));
        redisTemplate.opsForValue().set(REGION, regionMap);
    }

    public Result latestNews() {
        List<NewsDO> newsDOS = Optional.ofNullable(newsDAO.selectLatest(LATEST_NUM)).orElse(Collections.emptyList());
        List<NewsDTO> collect = newsDOS.stream()
                .map(newsDO -> {
                    NewsDTO newsDTO = new NewsDTO();
                    newsDTO.setId(newsDO.getId());
                    newsDTO.setTitle(newsDO.getTitle());
                    newsDTO.setShowTime(DateUtils.format(newsDO.getShowTime(), DateUtils.YYYY_MM_DD));
                    return newsDTO;
                }).collect(Collectors.toList());
        return Result.success(collect);
    }

    public Result latestApplication() {
        List<ApplicationDO> applicationDOS = Optional.ofNullable(applicationDAO.selectLatest(LATEST_NUM)).orElse(Collections.emptyList());
        List<LatestApplicationDTO> latestApplicationDTOS = Lists.newArrayList();
        for (ApplicationDO applicationDO : applicationDOS) {
            LatestApplicationDTO latestApplicationDTO = new LatestApplicationDTO();
            latestApplicationDTO.setTeamName(applicationDO.getTeamName());
            latestApplicationDTO.setTime(DateUtils.format(applicationDO.getCreateTime(), DateUtils.YYYY_MM_DD));
            latestApplicationDTOS.add(latestApplicationDTO);
        }
        return Result.success(latestApplicationDTOS);
    }


}
